﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using Matchingo.Common.Types.Types;

namespace Matchingo.Common.Types.Extensions
{
    public static class CardSetExtensions
    {
        private static Random RandomGenerator = new Random();

        /// <summary>
        /// Shuffles the cards collection of this CardSet into random order
        /// </summary>
        public static ObservableCollection<Card> Shuffle(this ObservableCollection<Card> cards)
        {
            #region Sanity Checks

            // we can't shuffle if there are no cards, or less than 2
            if (cards == null || cards.Count < 2)
                return cards;

            #endregion

            // iterate through however many cards there are, remove a random one from our cloned deck
            // and add it to our new deck we will return
            ObservableCollection<Card> shuffledDeck = new ObservableCollection<Card>();
            int cardCount = cards.Count;
            for (int ix = 0; ix < cardCount; ix++)
            {
                int randomIndex = RandomGenerator.Next(cards.Count - 1);
                shuffledDeck.Add(cards[randomIndex]);
                cards.RemoveAt(randomIndex);
            }

            cards = shuffledDeck;
            return cards;
        }

        public static List<Card> GetCardsNotMatched(this ObservableCollection<Card> cards)
        {
            var cardsNotMatched = (from c in cards
                                   where !c.IsMatched
                                   select c);
            return cardsNotMatched.ToList();
        }

        public static List<Card> GetCardsMatched(this ObservableCollection<Card> cards)
        {
            var cardsMatched = (from c in cards
                                where c.IsMatched
                                select c);
            return cardsMatched.ToList();
        }

        public static List<Card> GetCardsFaceUp(this ObservableCollection<Card> cards)
        {
            var cardsFaceUp = (from c in cards
                               where c.VisibleSide == Card.CardSide.Front
                               select c);
            return cardsFaceUp.ToList();
        }

        public static List<Card> GetCardsFaceDown(this ObservableCollection<Card> cards)
        {
            var cardsFaceDown = (from c in cards
                                 where c.VisibleSide == Card.CardSide.Rear
                                 select c);
            return cardsFaceDown.ToList();
        }

        /// <summary>
        /// Any cards which are face up but not matched
        /// </summary>
        public static List<Card> GetCardsFaceUpNotMatched(this ObservableCollection<Card> cards)
        {
            var cardsFaceUpNotMatched = (from c in cards
                                         where c.VisibleSide == Card.CardSide.Front && !c.IsMatched
                                         select c);
            return cardsFaceUpNotMatched.ToList();
        }

        /// <summary>
        /// Flip any cards which are face up to be face down, this will fire any card flip events
        /// </summary>
        public static void FlipAllCardsNotMatchedToRear(this ObservableCollection<Card> cards)
        {
            foreach (Card c in cards)
            {
                if (c.VisibleSide == Card.CardSide.Front && !c.IsMatched)
                {
                    c.Flip();
                }
            }
        }

        /// <summary>
        /// Gets a card with a given uniqueID
        /// Throws an ArgumentNullException if the card requested cant be found
        /// </summary>
        /// <param name="cards">The collection of cards from which to find a given ID</param>
        /// <param name="uniqueID">The uniqueID of the card being requested, unique even against match</param>
        /// <returns>The card matching the requested uniqueID</returns>
        public static Card GetCard(this ObservableCollection<Card> cards, Guid uniqueID)
        {
            var match = cards.FirstOrDefault(c => c.UniqueID == uniqueID);

            if (match == null)
                throw new ArgumentException("CarSet.GetCard() - Requested uniqueID not a part of this CardSet");

            return match;
        }
    }
}
